package org.srunpass.bitmap.repo;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

import org.srunpass.bitmap.annotations.IndexField;
import org.srunpass.bitmap.model.BitMapModel;

/**
 * @author srunpass
 * @version Id: BitMapEngine, v 0.1 2025/6/30 13:17 srunpass Exp $
 */
public class BitMapRepo<T extends BitMapModel> {

    private final Map<String, Map<Object, BitSet>> fieldIndexMap = new ConcurrentHashMap<>();
    private final Map<Object, Integer>             idToIndex     = new ConcurrentHashMap<>();
    private final List<T>                          objects       = new CopyOnWriteArrayList<>();
    private final AtomicInteger                    indexCounter  = new AtomicInteger(0);

    private final List<Field>                      indexFields   = new ArrayList<>();

    public BitMapRepo(Class<T> clazz) {
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(IndexField.class)) {
                field.setAccessible(true);
                indexFields.add(field);
                fieldIndexMap.put(field.getName(), new ConcurrentHashMap<Object, BitSet>());
            }
        }
    }

    public synchronized void upsert(T obj) throws IllegalAccessException {
        if (obj.isDelete()) {
            this.delete(obj);
            return;
        }
        Object id = obj.getId();
        Integer index = idToIndex.get(id);
        if (index != null) {
            removeByIndex(index);
        } else {
            index = indexCounter.getAndIncrement();
        }

        // ensure size
        while (objects.size() <= index) {
            objects.add(null);
        }
        objects.set(index, obj);
        idToIndex.put(id, index);

        for (Field field : indexFields) {
            Object value = field.get(obj);
            if (value == null)
                continue;

            IndexField anno = field.getAnnotation(IndexField.class);
            if (anno.multiValue() && value instanceof Collection) {
                Collection<?> col = (Collection<?>) value;
                for (Object val : col) {
                    addToIndex(field.getName(), val, index);
                }
            } else {
                addToIndex(field.getName(), value, index);
            }
        }
    }

    private void delete(T obj) {
        Object id = obj.getId();
        Integer index = idToIndex.get(id);
        if (index != null) {
            removeByIndex(index);
            objects.set(index, null);
            idToIndex.remove(id);
        }
    }

    private void addToIndex(String fieldName, Object value, int index) {
        Map<Object, BitSet> valueMap = fieldIndexMap.get(fieldName);
        BitSet bitSet = valueMap.get(value);
        if (bitSet == null) {
            bitSet = new BitSet();
            valueMap.put(value, bitSet);
        }
        bitSet.set(index);
    }

    private void removeByIndex(int index) {
        for (Map<Object, BitSet> map : fieldIndexMap.values()) {
            for (BitSet bitSet : map.values()) {
                bitSet.clear(index);
            }
        }
    }

    /**
     * 获取对象
     * @param result
     * @return
     */
    public List<T> getByIndex(BitSet result) {
        List<T> matched = new ArrayList<>();
        for (int i = result.nextSetBit(0); i >= 0; i = result.nextSetBit(i + 1)) {
            matched.add(objects.get(i));
        }
        return matched;
    }

    public BitSet query(String field, Object... vals) {
        BitSet result = new BitSet();
        Map<Object, BitSet> valueMap = fieldIndexMap.get(field);
        if (valueMap == null) {
            return result;
        }
        for (Object val : vals) {
            BitSet bitSet = valueMap.get(val);
            if (bitSet != null) {
                result.or(bitSet);
            }
        }
        return result;
    }
}